#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <thread>
#include <vector>
#include "LockGuard.hpp"
using namespace std;

// #define __VERSION1__
#define __VERSION2__

#define NUM 4
int tickets = 5000; // 模拟多个线程进行抢票
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
class threadData
{
public:
    threadData(int number /*,pthread_mutex_t *mutex*/)
    {
        threadname = "thread-" + to_string(number);
        // lock=mutex;
    }

public:
    string threadname;
    // pthread_mutex_t *lock;
};

#ifdef __VERSION1__
void *GetTickets(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    const char *name = td->threadname.c_str();
    while (true)
    {
        // 线程对于锁的竞争能力可能不同
        pthread_mutex_lock(&lock); // 执行流申请锁成功向后执行，失败必须阻塞等待
        if (tickets > 0)
        {
            usleep(1000); // 假设抢票需要1000us
            printf("who=%s,get a ticket:%d\n", name, tickets);
            tickets--;
            pthread_mutex_unlock(&lock);
        }
        else
        {
            pthread_mutex_unlock(&lock);
            break;
        }
        usleep(13); // 模拟抢到票后的后续操作....
    }
    printf("%s ... quit\n", name);
    return nullptr;
}
#endif

#ifdef __VERSION2__
void *GetTickets(void *args)
{
    threadData *td = static_cast<threadData *>(args);
    const char *name = td->threadname.c_str();
    while (true)
    {
        {  //明确临界区
            LockGuard lockguard(&lock); // 封装的LockGuard对象,自动进行加锁解锁的操作
            if (tickets > 0)
            {
                usleep(1000); // 假设抢票需要1000us
                printf("who=%s,get a ticket:%d\n", name, tickets);
                tickets--;
            }
            else
                break;
        }

        usleep(13); // 模拟抢到票后的后续操作....
    }
    printf("%s ... quit\n", name);
    return nullptr;
}
#endif

int main()
{
    pthread_mutex_t lock;
    pthread_mutex_init(&lock, nullptr);
    vector<pthread_t> tids;
    vector<threadData *> thread_datas;
    for (int i = 1; i <= NUM; i++)
    {
        pthread_t tid;
        threadData *td = new threadData(i /*,&lock*/);
        thread_datas.push_back(td);
        pthread_create(&tid, nullptr, GetTickets, thread_datas[i - 1]);
        tids.push_back(tid);
    }
    for (auto threadid : tids)
    {
        pthread_join(threadid, nullptr);
    }
    for (auto td : thread_datas)
    {
        delete td;
    }
    pthread_mutex_destroy(&lock);
    return 0;
}
